"""
资金费率策略

实现基于资金费率的套利策略，支持合约现货对冲
通过监控永续合约资金费率，在费率较高时做空合约做多现货，在费率较低时反向操作
"""

import time
import asyncio
from typing import Dict, List, Any, Optional, Tuple
from datetime import datetime, timedelta
from .base_strategy import BaseStrategy
from ..core.exceptions import StrategyError, ValidationError


class FundingRateStrategy(BaseStrategy):
    """资金费率套利策略 - 支持合约现货对冲"""
    
    def __init__(self, config: Dict[str, Any], exchange_adapter, 
                 order_manager, account_manager, data_manager):
        """
        初始化资金费率策略
        
        Args:
            config: 策略配置
            exchange_adapter: 交易所适配器
            order_manager: 订单管理器
            account_manager: 账户管理器
            data_manager: 数据管理器
        """
        super().__init__(config, exchange_adapter, order_manager, account_manager, data_manager)
        
        # 资金费率策略参数
        self.funding_rate_threshold = config.get('funding_rate_threshold', 0.01)  # 1%
        self.min_profit_rate = config.get('min_profit_rate', 0.005)  # 0.5%
        self.max_position_ratio = config.get('max_position_ratio', 0.8)  # 最大仓位比例
        self.hedge_ratio = config.get('hedge_ratio', 1.0)  # 对冲比例
        
        # 合约配置
        self.futures_symbol = config.get('futures_symbol', 'BTC-USDT-SWAP')
        self.spot_symbol = config.get('spot_symbol', 'BTC-USDT')
        self.leverage = config.get('leverage', 3)
        
        # 时间配置
        self.funding_interval = config.get('funding_interval', 8)  # 资金费率结算间隔(小时)
        self.check_interval = config.get('check_interval', 60)  # 检查间隔(秒)
        self.hold_time_min = config.get('hold_time_min', 30)  # 最小持仓时间(分钟)
        
        # 风险控制
        self.max_drawdown = config.get('max_drawdown', 0.05)  # 最大回撤
        self.stop_loss_ratio = config.get('stop_loss_ratio', 0.02)  # 止损比例
        self.emergency_close_spread = config.get('emergency_close_spread', 0.03)  # 紧急平仓价差
        
        # 策略状态
        self.current_position = {
            'futures': {'side': None, 'size': 0, 'entry_price': 0},
            'spot': {'side': None, 'size': 0, 'entry_price': 0}
        }
        self.last_funding_rate = 0.0
        self.last_funding_time = None
        self.position_entry_time = None
        self.total_funding_earned = 0.0
        
        # 历史数据
        self.funding_rate_history = []
        self.spread_history = []
        self.pnl_history = []
        
        self.logger.info(f"资金费率策略初始化完成: {self.futures_symbol} <-> {self.spot_symbol}")
    
    def initialize(self) -> bool:
        """初始化资金费率策略"""
        try:
            # 验证交易对
            if not self._validate_symbols():
                return False
            
            # 检查账户余额
            if not self._check_sufficient_balance():
                return False
            
            # 获取初始资金费率
            self._update_funding_rate()
            
            # 设置定时任务
            self._setup_periodic_tasks()
            
            self.logger.info("资金费率策略初始化成功")
            return True
            
        except Exception as e:
            self.logger.error(f"资金费率策略初始化失败: {e}")
            return False
    
    def _validate_symbols(self) -> bool:
        """验证交易对"""
        try:
            # 简化验证逻辑，只检查交易对格式
            if not self.futures_symbol or not self.spot_symbol:
                self.logger.error("交易对不能为空")
                return False
            
            # 检查交易对格式
            if '-' not in self.futures_symbol or '-' not in self.spot_symbol:
                self.logger.error("交易对格式错误")
                return False
            
            self.logger.info(f"交易对验证成功: {self.futures_symbol} <-> {self.spot_symbol}")
            return True
            
        except Exception as e:
            self.logger.error(f"验证交易对失败: {e}")
            return False
    
    def _check_sufficient_balance(self) -> bool:
        """检查账户余额是否充足"""
        try:
            # 获取账户余额
            spot_balance = self.account_manager.get_spot_balance(self.exchange)
            futures_balance = self.account_manager.get_futures_balance(self.exchange)
            
            # 计算所需资金
            current_price = self.get_latest_price()
            if not current_price:
                self.logger.error("无法获取当前价格")
                return False
                
            required_spot_balance = current_price * self.max_position_ratio
            required_futures_margin = required_spot_balance / self.leverage
            
            # 检查现货余额
            if not spot_balance:
                self.logger.error("无法获取现货余额")
                return False
                
            usdt_balance = spot_balance.get('USDT', {}).get('available', 0)
            if usdt_balance < required_spot_balance:
                self.logger.error(f"现货USDT余额不足: {usdt_balance} < {required_spot_balance}")
                return False
            
            # 检查合约保证金
            if not futures_balance:
                self.logger.error("无法获取合约余额")
                return False
                
            futures_usdt = futures_balance.get('USDT', {}).get('available', 0)
            if futures_usdt < required_futures_margin:
                self.logger.error(f"合约保证金不足: {futures_usdt} < {required_futures_margin}")
                return False
            
            self.logger.info(f"余额检查通过 - 现货: {usdt_balance}, 合约: {futures_usdt}")
            return True
            
        except Exception as e:
            self.logger.error(f"检查余额失败: {e}")
            return False
    
    def _update_funding_rate(self) -> None:
        """更新资金费率"""
        try:
            funding_info = self.exchange.get_funding_rate(self.futures_symbol)
            if funding_info:
                self.last_funding_rate = funding_info.get('funding_rate', 0)
                self.last_funding_time = funding_info.get('funding_time')
                
                # 记录历史数据
                self.funding_rate_history.append({
                    'timestamp': time.time(),
                    'rate': self.last_funding_rate,
                    'next_funding_time': self.last_funding_time
                })
                
                # 保持历史数据在合理范围内
                if len(self.funding_rate_history) > 1000:
                    self.funding_rate_history = self.funding_rate_history[-500:]
                
                self.logger.debug(f"资金费率更新: {self.last_funding_rate:.6f}")
            
        except Exception as e:
            self.logger.error(f"更新资金费率失败: {e}")
    
    def _setup_periodic_tasks(self) -> None:
        """设置定时任务"""
        # 这里可以设置定时更新资金费率、检查仓位等任务
        pass
    
    async def on_tick(self, data: Dict[str, Any]) -> None:
        """处理价格数据"""
        try:
            symbol = data.get('symbol')
            price = data.get('price', 0)
            
            if symbol == self.futures_symbol or symbol == self.spot_symbol:
                # 更新价差历史
                self._update_spread_history()
                
                # 检查交易机会
                await self._check_trading_opportunity()
                
                # 检查现有仓位
                await self._monitor_positions()
                
                # 风险控制
                await self._risk_management()
        
        except Exception as e:
            self.logger.error(f"处理价格数据失败: {e}")
    
    def _update_spread_history(self) -> None:
        """更新价差历史"""
        try:
            futures_price = self.exchange.get_latest_price(self.futures_symbol)
            spot_price = self.exchange.get_latest_price(self.spot_symbol)
            
            if futures_price and spot_price:
                spread = (futures_price - spot_price) / spot_price
                self.spread_history.append({
                    'timestamp': time.time(),
                    'futures_price': futures_price,
                    'spot_price': spot_price,
                    'spread': spread
                })
                
                # 保持历史数据在合理范围内
                if len(self.spread_history) > 1000:
                    self.spread_history = self.spread_history[-500:]
        
        except Exception as e:
            self.logger.error(f"更新价差历史失败: {e}")
    
    async def _check_trading_opportunity(self) -> None:
        """检查交易机会"""
        try:
            # 如果已有仓位，不开新仓
            if self._has_position():
                return
            
            # 更新资金费率
            self._update_funding_rate()
            
            # 计算预期收益
            expected_profit = self._calculate_expected_profit()
            
            if expected_profit > self.min_profit_rate:
                if self.last_funding_rate > self.funding_rate_threshold:
                    # 资金费率为正且较高，做空合约做多现货
                    await self._open_short_futures_long_spot()
                elif self.last_funding_rate < -self.funding_rate_threshold:
                    # 资金费率为负且较低，做多合约做空现货
                    await self._open_long_futures_short_spot()
        
        except Exception as e:
            self.logger.error(f"检查交易机会失败: {e}")
    
    def _calculate_expected_profit(self) -> float:
        """计算预期收益"""
        try:
            # 基于资金费率计算预期收益
            hours_to_funding = self._get_hours_to_next_funding()
            if hours_to_funding <= 0:
                return 0
            
            # 年化资金费率
            annual_funding_rate = abs(self.last_funding_rate) * (365 * 24 / self.funding_interval)
            
            # 考虑持仓时间的收益
            holding_periods = max(1, self.hold_time_min / (self.funding_interval * 60))
            expected_profit = annual_funding_rate * holding_periods / (365 * 24 / self.funding_interval)
            
            return expected_profit
        
        except Exception as e:
            self.logger.error(f"计算预期收益失败: {e}")
            return 0
    
    def _get_hours_to_next_funding(self) -> float:
        """获取距离下次资金费率结算的小时数"""
        if not self.last_funding_time:
            return 0
        
        try:
            next_funding = datetime.fromisoformat(self.last_funding_time.replace('Z', '+00:00'))
            now = datetime.utcnow()
            delta = next_funding - now
            return max(0, delta.total_seconds() / 3600)
        
        except Exception as e:
            self.logger.error(f"计算资金费率时间失败: {e}")
            return 0
    
    async def _open_short_futures_long_spot(self) -> None:
        """做空合约做多现货"""
        try:
            current_price = self.get_latest_price()
            position_size = self._calculate_position_size(current_price)
            
            # 做空合约
            futures_order = self.place_futures_order(
                'market', 'sell', position_size, self.leverage
            )
            
            # 做多现货
            spot_order = self.place_spot_order(
                'market', 'buy', position_size
            )
            
            if futures_order and spot_order:
                self.current_position = {
                    'futures': {'side': 'short', 'size': position_size, 'entry_price': current_price},
                    'spot': {'side': 'long', 'size': position_size, 'entry_price': current_price}
                }
                self.position_entry_time = time.time()
                
                self.logger.info(f"开仓成功 - 做空合约做多现货: {position_size} @ {current_price}")
        
        except Exception as e:
            self.logger.error(f"开仓失败: {e}")
    
    async def _open_long_futures_short_spot(self) -> None:
        """做多合约做空现货"""
        try:
            current_price = self.get_latest_price()
            position_size = self._calculate_position_size(current_price)
            
            # 做多合约
            futures_order = self.place_futures_order(
                'market', 'buy', position_size, self.leverage
            )
            
            # 做空现货（需要借币）
            spot_order = self.place_spot_order(
                'market', 'sell', position_size
            )
            
            if futures_order and spot_order:
                self.current_position = {
                    'futures': {'side': 'long', 'size': position_size, 'entry_price': current_price},
                    'spot': {'side': 'short', 'size': position_size, 'entry_price': current_price}
                }
                self.position_entry_time = time.time()
                
                self.logger.info(f"开仓成功 - 做多合约做空现货: {position_size} @ {current_price}")
        
        except Exception as e:
            self.logger.error(f"开仓失败: {e}")
    
    def _calculate_position_size(self, price: float) -> float:
        """计算仓位大小"""
        try:
            # 获取可用余额
            balance = self.account_manager.get_total_balance()
            max_position_value = balance * self.max_position_ratio
            
            # 计算仓位大小
            position_size = max_position_value / price
            
            # 应用对冲比例
            position_size *= self.hedge_ratio
            
            return round(position_size, 6)
        
        except Exception as e:
            self.logger.error(f"计算仓位大小失败: {e}")
            return 0
    
    def _has_position(self) -> bool:
        """检查是否有持仓"""
        return (self.current_position['futures']['size'] > 0 or 
                self.current_position['spot']['size'] > 0)
    
    async def _monitor_positions(self) -> None:
        """监控现有仓位"""
        if not self._has_position():
            return
        
        try:
            # 检查是否到达最小持仓时间
            if self._should_close_position():
                await self._close_all_positions()
        
        except Exception as e:
            self.logger.error(f"监控仓位失败: {e}")
    
    def _should_close_position(self) -> bool:
        """判断是否应该平仓"""
        if not self.position_entry_time:
            return False
        
        # 检查最小持仓时间
        holding_time = (time.time() - self.position_entry_time) / 60  # 分钟
        if holding_time < self.hold_time_min:
            return False
        
        # 检查资金费率变化
        if abs(self.last_funding_rate) < self.funding_rate_threshold * 0.5:
            return True
        
        # 检查距离下次资金费率结算时间
        hours_to_funding = self._get_hours_to_next_funding()
        if hours_to_funding < 0.5:  # 30分钟内结算
            return True
        
        return False
    
    async def _close_all_positions(self) -> None:
        """平仓所有仓位"""
        try:
            # 平仓合约
            if self.current_position['futures']['size'] > 0:
                side = 'buy' if self.current_position['futures']['side'] == 'short' else 'sell'
                self.place_futures_order(
                    'market', side, self.current_position['futures']['size'], self.leverage
                )
            
            # 平仓现货
            if self.current_position['spot']['size'] > 0:
                side = 'sell' if self.current_position['spot']['side'] == 'long' else 'buy'
                self.place_spot_order(
                    'market', side, self.current_position['spot']['size']
                )
            
            # 计算收益
            self._calculate_position_pnl()
            
            # 重置仓位
            self.current_position = {
                'futures': {'side': None, 'size': 0, 'entry_price': 0},
                'spot': {'side': None, 'size': 0, 'entry_price': 0}
            }
            self.position_entry_time = None
            
            self.logger.info("平仓完成")
        
        except Exception as e:
            self.logger.error(f"平仓失败: {e}")
    
    def _calculate_position_pnl(self) -> float:
        """计算仓位盈亏"""
        try:
            if not self._has_position():
                return 0
            
            current_price = self.get_latest_price()
            entry_price = self.current_position['futures']['entry_price']
            position_size = self.current_position['futures']['size']
            
            # 计算合约盈亏
            if self.current_position['futures']['side'] == 'long':
                futures_pnl = (current_price - entry_price) * position_size
            else:
                futures_pnl = (entry_price - current_price) * position_size
            
            # 计算现货盈亏
            if self.current_position['spot']['side'] == 'long':
                spot_pnl = (current_price - entry_price) * position_size
            else:
                spot_pnl = (entry_price - current_price) * position_size
            
            # 总盈亏
            total_pnl = futures_pnl + spot_pnl
            
            # 记录盈亏历史
            self.pnl_history.append({
                'timestamp': time.time(),
                'futures_pnl': futures_pnl,
                'spot_pnl': spot_pnl,
                'total_pnl': total_pnl,
                'funding_earned': self.total_funding_earned
            })
            
            return total_pnl
        
        except Exception as e:
            self.logger.error(f"计算盈亏失败: {e}")
            return 0
    
    async def _risk_management(self) -> None:
        """风险管理"""
        if not self._has_position():
            return
        
        try:
            current_pnl = self._calculate_position_pnl()
            entry_value = (self.current_position['futures']['entry_price'] * 
                          self.current_position['futures']['size'])
            
            # 止损检查
            if entry_value > 0 and abs(current_pnl / entry_value) > self.stop_loss_ratio:
                self.logger.warning(f"触发止损: PnL={current_pnl}, 比例={current_pnl/entry_value:.4f}")
                await self._close_all_positions()
                return
            
            # 价差异常检查
            if len(self.spread_history) > 0:
                current_spread = abs(self.spread_history[-1]['spread'])
                if current_spread > self.emergency_close_spread:
                    self.logger.warning(f"价差异常，紧急平仓: spread={current_spread:.4f}")
                    await self._close_all_positions()
        
        except Exception as e:
            self.logger.error(f"风险管理失败: {e}")
    
    def place_futures_order(self, order_type: str, side: str, amount: float, 
                           leverage: int) -> Optional[str]:
        """下合约订单"""
        try:
            return self.exchange.futures_place_order(
                self.futures_symbol, order_type, side, amount, leverage
            )
        except Exception as e:
            self.logger.error(f"下合约订单失败: {e}")
            return None
    
    def place_spot_order(self, order_type: str, side: str, amount: float) -> Optional[str]:
        """下现货订单"""
        try:
            return self.exchange.spot_place_order(
                self.spot_symbol, order_type, side, amount
            )
        except Exception as e:
            self.logger.error(f"下现货订单失败: {e}")
            return None
    
    async def on_order_update(self, order: Dict[str, Any]) -> None:
        """处理订单更新"""
        try:
            order_id = order.get('order_id')
            status = order.get('status')
            symbol = order.get('symbol')
            
            if status == 'filled':
                self.logger.info(f"订单成交: {order_id} - {symbol}")
                
                # 更新资金费率收益
                if symbol == self.futures_symbol:
                    self._update_funding_earned()
        
        except Exception as e:
            self.logger.error(f"处理订单更新失败: {e}")
    
    def _update_funding_earned(self) -> None:
        """更新资金费率收益"""
        try:
            if self._has_position():
                position_size = self.current_position['futures']['size']
                funding_payment = position_size * self.last_funding_rate
                
                # 根据仓位方向调整收益
                if self.current_position['futures']['side'] == 'short':
                    funding_payment = -funding_payment
                
                self.total_funding_earned += funding_payment
                self.logger.debug(f"资金费率收益更新: {funding_payment:.6f}")
        
        except Exception as e:
            self.logger.error(f"更新资金费率收益失败: {e}")
    
    def get_strategy_status(self) -> Dict[str, Any]:
        """获取策略状态"""
        return {
            'strategy_name': self.strategy_name,
            'running': self.running,
            'current_position': self.current_position,
            'last_funding_rate': self.last_funding_rate,
            'total_funding_earned': self.total_funding_earned,
            'position_entry_time': self.position_entry_time,
            'current_pnl': self._calculate_position_pnl() if self._has_position() else 0,
            'funding_rate_history_count': len(self.funding_rate_history),
            'spread_history_count': len(self.spread_history),
            'pnl_history_count': len(self.pnl_history)
        }
    
    def get_performance_stats(self) -> Dict[str, Any]:
        """获取策略绩效统计"""
        try:
            if not self.pnl_history:
                return {}
            
            pnl_values = [p['total_pnl'] for p in self.pnl_history]
            funding_values = [p['funding_earned'] for p in self.pnl_history]
            
            return {
                'total_trades': len(self.pnl_history),
                'total_pnl': sum(pnl_values),
                'total_funding_earned': sum(funding_values),
                'avg_pnl_per_trade': sum(pnl_values) / len(pnl_values) if pnl_values else 0,
                'max_pnl': max(pnl_values) if pnl_values else 0,
                'min_pnl': min(pnl_values) if pnl_values else 0,
                'win_rate': len([p for p in pnl_values if p > 0]) / len(pnl_values) if pnl_values else 0,
                'avg_funding_rate': sum([h['rate'] for h in self.funding_rate_history]) / len(self.funding_rate_history) if self.funding_rate_history else 0,
                'avg_spread': sum([h['spread'] for h in self.spread_history]) / len(self.spread_history) if self.spread_history else 0
            }
        
        except Exception as e:
            self.logger.error(f"获取绩效统计失败: {e}")
            return {}